package cz.muni.fi.pa165.musiclibrary.musiclibraryweb.components;

import java.text.Collator;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.event.Broadcast;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.IChoiceRenderer;
import org.apache.wicket.markup.html.form.RequiredTextField;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.apache.wicket.validation.IValidatable;
import org.apache.wicket.validation.IValidator;
import org.apache.wicket.validation.ValidationError;
import org.joda.time.Duration;

import com.google.common.collect.Lists;

import cz.muni.fi.pa165.musiclibrary.api.service.IAlbumService;
import cz.muni.fi.pa165.musiclibrary.api.service.IArtistService;
import cz.muni.fi.pa165.musiclibrary.api.service.IGenreService;
import cz.muni.fi.pa165.musiclibrary.api.service.ISongService;
import cz.muni.fi.pa165.musiclibrary.api.service.auth.IUser;
import cz.muni.fi.pa165.musiclibrary.api.service.dto.IAlbumDTO;
import cz.muni.fi.pa165.musiclibrary.api.service.dto.IArtistDTO;
import cz.muni.fi.pa165.musiclibrary.api.service.dto.IGenreDTO;
import cz.muni.fi.pa165.musiclibrary.api.service.dto.ISongDTO;
import cz.muni.fi.pa165.musiclibrary.musiclibraryweb.MusicLibrarySession;
import cz.muni.fi.pa165.musiclibrary.musiclibraryweb.utils.DTOFactory;

/**
 *
 * @author Maros Kucbel
 * @date 2012-11-26T21:33:25+0100
 */
public final class SongFormPanel extends Panel {

    private ISongDTO song;
    @SpringBean
    private IAlbumService albumService;
    @SpringBean
    private ISongService songService;
    @SpringBean
    private IGenreService genreService;
    @SpringBean
    private IArtistService artistService;
    
    public SongFormPanel(String id) {
        this(id, DTOFactory.create(ISongDTO.class));
    }
    
    public SongFormPanel(String id, ISongDTO song) {
        super(id);
        this.song = song;
    }

    @Override
    protected void onInitialize() {
        super.onInitialize();
        this.initComponents();
    }
    
    private void initComponents() {
        final FeedbackPanel feedbackPanel = new FeedbackPanel("feedbackPanel");
        feedbackPanel.setOutputMarkupId(true);
        this.add(feedbackPanel);
        Form<ISongDTO> form = new Form<ISongDTO>(ComponentIDs.FORM, Model.of(this.song)) {

            @Override
            protected void onSubmit() {
                ISongDTO song = this.getModelObject();
                try {
                    IUser user = MusicLibrarySession.get().getUser();
                    String userName = user != null ? user.getUserName() : null;
                    if (song.getIdSong() != null) {
                        SongFormPanel.this.songService.update(song, userName);
                    } else {
                        SongFormPanel.this.songService.create(song, userName);
                    }
                    this.send(this.getPage(), Broadcast.BREADTH, new FormSubmittedEvent(RequestCycle.get().find(AjaxRequestTarget.class)));
                } catch (Exception ex) {
                    this.error(new StringResourceModel("database.error", this, null).getObject());
                    RequestCycle.get().find(AjaxRequestTarget.class).add(feedbackPanel);
                }
            }
            
        };

        RequiredTextField<String> titleField = new RequiredTextField<String>(ComponentIDs.TITLE, new PropertyModel<String>(form.getModelObject(), "title"));
        titleField.setOutputMarkupId(true);
        form.add(titleField);

        List<IArtistDTO> artists = Lists.newArrayList(this.artistService.getAll());
        Collections.sort(artists, new Comparator<IArtistDTO>() {
            private Collator collator = Collator.getInstance(SongFormPanel.this.getLocale());
            @Override
            public int compare(IArtistDTO o1, IArtistDTO o2) {
                return this.collator.compare(o1.getName(), o2.getName());
            }
        });
        PropertyModel<IArtistDTO> artistModel = new PropertyModel<IArtistDTO>(form.getModelObject(), "artist");
        DropDownChoice<IArtistDTO> artistChoice = new DropDownChoice<IArtistDTO>(ComponentIDs.ARTIST, artistModel, artists, 
                new IChoiceRenderer<IArtistDTO>() {

            @Override
            public Object getDisplayValue(IArtistDTO artist) {
                return artist.getName();
            }

            @Override
            public String getIdValue(IArtistDTO artist, int index) {
                return artist.getIdArtist().toString();
            }
        });
        artistChoice.setNullValid(false);
        artistChoice.setRequired(true);
        form.add(artistChoice);

        List<IGenreDTO> genres = Lists.newArrayList(this.genreService.getAll());
        Collections.sort(genres, new Comparator<IGenreDTO>() {
            private Collator collator = Collator.getInstance(SongFormPanel.this.getLocale());
            @Override
            public int compare(IGenreDTO o1, IGenreDTO o2) {
                return this.collator.compare(o1.getName(), o2.getName());
            }
        });
        PropertyModel<IGenreDTO> genreModel = new PropertyModel<IGenreDTO>(form.getModelObject(), "genre");
        DropDownChoice<IGenreDTO> genreChoice = new DropDownChoice<IGenreDTO>(ComponentIDs.GENRE, genreModel, genres,
                new IChoiceRenderer<IGenreDTO>() {

            @Override
            public Object getDisplayValue(IGenreDTO genre) {
                return genre.getName();
            }

            @Override
            public String getIdValue(IGenreDTO genre, int index) {
                return genre.getIdGenre().toString();
            }
        });
        genreChoice.setNullValid(false);
        genreChoice.setRequired(true);
        form.add(genreChoice);

        List<IAlbumDTO> albums = Lists.newArrayList(this.albumService.getAll());
        Collections.sort(albums, new Comparator<IAlbumDTO>() {
            private Collator collator = Collator.getInstance(SongFormPanel.this.getLocale());
            @Override
            public int compare(IAlbumDTO o1, IAlbumDTO o2) {
                return this.collator.compare(o1.getTitle(), o2.getTitle());
            }
        });
        PropertyModel<IAlbumDTO> albumModel = new PropertyModel<IAlbumDTO>(form.getModelObject(), "album");
        DropDownChoice<IAlbumDTO> albumChoice = new DropDownChoice<IAlbumDTO>(ComponentIDs.ALBUM, albumModel, albums,
                new IChoiceRenderer<IAlbumDTO>() {

            @Override
            public Object getDisplayValue(IAlbumDTO album) {
                return album.getTitle();
            }

            @Override
            public String getIdValue(IAlbumDTO genre, int index) {
                return genre.getIdAlbum().toString();
            }
        });
        albumChoice.setNullValid(false);
        albumChoice.setRequired(true);
        form.add(albumChoice);

        TextField<Integer> trackNumberField = new TextField<Integer>(ComponentIDs.TRACK_NUMBER, 
                new PropertyModel<Integer>(form.getModelObject(), "trackNumber"));
        trackNumberField.setOutputMarkupId(true);
        trackNumberField.add(this.getPositiveIntegerValidator(new StringResourceModel("trackNumber.IConverter.Integer", this, null).getObject()));
        form.add(trackNumberField);

        RequiredTextField<Integer> bitrateField = new RequiredTextField<Integer>(ComponentIDs.BITRATE, 
                new PropertyModel<Integer>(form.getModelObject(), "bitrate"));
        bitrateField.setOutputMarkupId(true);
        bitrateField.add(this.getPositiveIntegerValidator(new StringResourceModel("bitrate.IConverter.Integer", this, null).getObject()));
        form.add(bitrateField);

        TextField<Duration> lengthField = new TextField<Duration>(ComponentIDs.LENGTH, 
                new PropertyModel<Duration>(form.getModelObject(), "length"));
        form.add(lengthField);

        TextArea<String> commentField = new TextArea<String>(ComponentIDs.COMMENT, 
                new PropertyModel<String>(form.getModelObject(), "comment"));
        form.add(commentField);

        form.add(new AjaxSubmitLinkWithErrorClass(ComponentIDs.SUBMIT_BUTTON, form) {

            @Override
            protected void onError(AjaxRequestTarget target, Form<?> form) {
                target.add(feedbackPanel);
                super.onError(target, form);
            }
        });
        this.add(form);
    }
    /**
     * Returns validator that checks whether input value is a positive integer.
     * @param errorMessage Error message that will be shown.
     * @return Validator.
     */
    private IValidator<Integer> getPositiveIntegerValidator(final String errorMessage) {
        return new IValidator<Integer>() {
            @Override
            public void validate(IValidatable<Integer> validatable) {
                if (validatable.getValue() != null && validatable.getValue().intValue() < 1) {
                    validatable.error(new ValidationError(errorMessage));
                }
            }
        };
    }
    
    private static final class ComponentIDs {
        private static final String FORM = "form";
        private static final String TITLE = "title";
        private static final String ARTIST = "artist";
        private static final String GENRE = "genre";
        private static final String ALBUM = "album";
        private static final String BITRATE = "bitrate";
        private static final String LENGTH = "songLength";
        private static final String TRACK_NUMBER = "trackNumber";
        private static final String COMMENT = "comment";
        private static final String SUBMIT_BUTTON = "submit";
    }
}